#include "io.h"

#include "template_set.h"
#include "ship_template.h"
#include "slot_template.h"

#include <fstream>
#include <iostream>
#include "json/reader.h"
#include "json/value.h"

using namespace std;

FiletypeError::FiletypeError(const std::string & what_arg) : runtime_error( what_arg ) {}

namespace IO {
	Json::Value loadJson( string _filename ) {
		Json::Value json;

		Json::Reader reader;
		ifstream file(_filename);

		bool result = reader.parse(file, json);
		file.close();
		if ( ! result ) {
			string error = "IO::loadJson(string)\n";
			error += "    Failed to load Json Value from " + _filename + "\n";
			error += "    Error: " + reader.getFormatedErrorMessages() + "\n";

			throw runtime_error(error);
		}
		return json;
	}

	Json::Value saveTemplate( const SlotTemplate & _slot ) {
		Json::Value slot;
		
		Directions dirs = _slot.getDirections();

		slot["Directions"][0u] = dirs.left;
		slot["Directions"][1u] = dirs.middle;
		slot["Directions"][2u] = dirs.right;

		slot["MaxHp"] = _slot.getMaxHp();
		slot["Cost"] = _slot.getCost();

		return slot;
	}
	
	Json::Value saveTemplate( const ShipTemplate & _ship ) {
		Json::Value ship;

		ship["Name"] = _ship.name;
		ship["Cost"] = _ship.getCost();
		ship["Class"] = _ship.getShipTemplateClass();
		ship["MaxHp"] = _ship.getMaxHp();
		ship["ControlTowers"] = _ship.getTowers();

		for ( unsigned int i = 0; i < 6; i++ ) {
			vector<SlotTemplate> temp = _ship.getSide(i);
			unsigned int j = 0;
			for ( auto &it : temp ) {
				ship["Sides"][i][j] = saveTemplate(it);
				j++;
			}
		}

		return ship;
	}

	Json::Value saveTemplate( const TemplateSet & _set ) {
		Json::Value set;

		set["Name"] = _set.name;

		for ( int i = 0; i < _set.getShipTemplatesNumber(); i++ )
			set["ShipTemplates"][i] = saveTemplate(*(_set.getShipTemplate(i)));

		return set;
	}

	Json::Value loadStandardShipTemplate( int _shipTemplateType ) {
		if ( _shipTemplateType < MIN_SHIP_TYPE ||
			 _shipTemplateType > MAX_SHIP_TYPE ) throw invalid_argument("IO::loadStandardShipTemplate(int)");

		// Derive the name of the chosen standard template	
		string designName;
		switch ( _shipTemplateType ) {
			case STANDARD_LIGHT_SHIP:
				designName = "Standard Light Ship";
				break;
			case STANDARD_MEDIUM_SHIP:
				designName = "Standard Medium Ship";
				break;
			case STANDARD_HEAVY_SHIP:
				designName = "Standard Heavy Ship";
				break;	
			default:
				throw invalid_argument("IO::loadStandardTemplate(int)");
		}

		// Create a reader and open/read the standard file with base templates
		Json::Value baseShips;
		try {
			baseShips = loadJson("data/standard_ships.json");
		}
		catch(runtime_error e) {
			string filename;
			cout << "Couldn't find the default template file at 'data/standard_ships.json'. Please enter the location of the file: ";
			getline(cin, filename); cout << "\n";
			baseShips = loadJson(filename);
		}


		// Look between the read templates for the one we need
		bool found = false;
		// This needs to be outside!
		unsigned i = 0;
		for ( ; i < baseShips.size(); i++ ) {
			if ( baseShips[i]["Type"] == "StandardTemplate" &&
				 baseShips[i]["Name"] == designName ) {
				found = true;
				break;
			}
		}

		if ( !found ) throw runtime_error("IO::loadStandardShipTemplate(int)\nBase ship not found!");

		// Return it
		return baseShips[i];	
	}

	Json::Value loadTemplateSet( string _filename, string _setName) {
		Json::Value sets;

		sets = loadJson(_filename);

		// We want a particular one
		if ( _setName != "" ) {
			bool found = false;
			// This needs to be outside!
			unsigned i = 0;
			for ( ; i < sets.size(); i++ ) {
				if ( sets[i]["Type"] == "TemplateSet" &&
					 sets[i]["Name"] == _setName ) {
					found = true;
					break;
				}
			}

			if ( !found ) throw FiletypeError("IO::loadTemplateSet(string,string)\n    Set not found!");

			return sets[i];
		}
		else {
			Json::Value realSets;

			for ( unsigned i = 0; i < sets.size(); i++ )
				if ( sets[i]["Type"] == "TemplateSet" )
					realSets.append(sets[i]);

			if ( !realSets.size() ) throw FiletypeError("IO::loadTemplateSet(string,string)\n    No TemplateSet found!");

			return realSets;
		}
	}

	Json::Value loadDeployment( string _filename, string _depName) {
		Json::Value deps;

		deps = loadJson(_filename);

		// We want a particular one
		if ( _depName != "" ) {
			bool found = false;
			// This needs to be outside!
			unsigned i = 0;
			for ( ; i < deps.size(); i++ ) {
				if ( deps[i]["Type"] == "Deployment" &&
					 deps[i]["Name"] == _depName ) {
					found = true;
					break;
				}
			}

			if ( !found ) throw FiletypeError("IO::loadDeployment(string,string)\n    Deployment not found!");

			return deps[i];
		}
		else {
			Json::Value realDeps;

			for ( unsigned i = 0; i < deps.size(); i++ )
				if ( deps[i]["Type"] == "Deployment" )
					realDeps.append(deps[i]);

			if ( !realDeps.size() ) throw FiletypeError("IO::loadDeployment(string,string)\n    No Deployment found!");

			return realDeps;
		}
	}
}
